.486
.model	flat, stdcall
option	casemap :none   ; case sensitive

include		base.inc
include		cb.inc

.code

 COMETHOD MACRO ppv,metoda
	mov  eax,ppv.lpVtbl   ; pointer to variable in DLL
	mov  eax,[eax]        ; pointer to VTable [in DLL]
	mov  eax,[eax.metoda] ; pointer to a given method
	push ppv              ; we must tell the method
                                ; who's calling [instance]
                                ; it is known as 'this' param.
	call eax              ; we call the method
 ENDM
 
start:
	invoke	GetModuleHandle, NULL
	mov	hInstance, eax
	
	invoke	cbCheckMutex
	mov	cbMutexID,eax
	
	invoke	cbGetVolume
	
	invoke	DialogBoxParam, hInstance, 101, 0, ADDR DlgProc, 0
	@end:
	invoke	ExitProcess, eax

DlgProc	proc	hWin	:DWORD,
		uMsg	:DWORD,
		wParam	:DWORD,
		lParam	:DWORD
	LOCAL	cbPlayPauseKeyText[32]:BYTE
	LOCAL	cbPreviousTrackKeyText[32]:BYTE
	LOCAL	cbNextTrackKeyText[32]:BYTE
	.if	uMsg == WM_COMMAND
		mov eax,wParam
		shr	eax,16
		mov ebx,eax
		mov eax,wParam
		shl	eax,16
		shr	eax,16
		;ebx contains the hiword of wParam, eax the loword
		.if	wParam == IDC_CBGO
			;Initialise the taskbar icon
			mov cbIconData.cbSize,25d
			mov eax,hWin
			mov cbIconData.hwnd,eax
			mov cbIconData.uID,1337d
			mov cbIconData.uFlags,NIF_ICON or NIF_MESSAGE
			mov cbIconData.uCallbackMessage,CB_ICON_MESSAGE
			invoke	LoadIcon,hInstance,IDC_CBICON
			mov cbhIcon,eax
			mov cbIconData.hIcon,eax
			mov cbIconData.szTip,0
			
			;Hide Window
			invoke	ShowWindow,hWin,SW_HIDE
			invoke	Shell_NotifyIcon,NIM_ADD,addr cbIconData
			mov	cbSet,1
			
			;Set the keyboard hook
			invoke	SetWindowsHookEx,WH_KEYBOARD_LL,addr cbKeyMon,hInstance,0
			mov cbKeyboardHookID,eax
			
			;Set the mouse hook if necessary (for quickvol)
			invoke	IsDlgButtonChecked,hWin,IDC_CBQUICKVOL
			.if	eax==1
				invoke	SetWindowsHookEx,WH_MOUSE_LL,addr cbMouseMon,hInstance,0
				mov cbMouseHookID,eax
			.endif
			
			;Set shuffle if necessary, this MUST always run because iTunes may have been (bean!) left with shuffle on etc
			invoke	IsDlgButtonChecked,hWin,IDC_CBSHUFFLE
			mov	cbShuffleSet,eax
			invoke	CreateThread,0,0,addr cbShuffle,0,0,addr cbThreadID
       		 .elseif	wParam== IDC_CBQUIT
			jmp @quit
		.elseif	eax==IDC_CBABOUT
			invoke	CreateThread,0,0,addr cbAbout,0,0,addr cbThreadID
		.elseif	eax==IDC_CBPLAY
			.if	ebx==EN_SETFOCUS
				mov ecx,cbSettingKey
				.if	ecx==1
					ret
				.endif
				
				;we are setting a key here, so flag it!
				mov cbSettingKey,1
				;Set text to 'press a key'..
				invoke	SetDlgItemText,hWin,IDC_CBPLAY,addr cbSetKeyMessage
				;get the keyboard focus off us!, if the user clicks off window, then sets a key, the cursor won't reland here..
				invoke	SendMessage,cbhWin,WM_SETFOCUS,0,0
				;Get a key...
				mov ecx,hWin
				mov	cbhWin,ecx
				invoke	CreateThread,0,0,addr cbGetKey,addr cbPlayPauseKey,0,addr cbThreadID
				
				;listen for a key...
				invoke	SetWindowsHookEx,WH_KEYBOARD_LL,addr cbGetKeyC,hInstance,0
				mov cbKeyboardHookID,eax
			.endif
		.elseif	eax==IDC_CBNEXT
			.if	ebx==EN_SETFOCUS
				mov ecx,cbSettingKey
				.if	ecx==1
					ret
				.endif
				
				;we are setting a key here, so flag it!
				mov cbSettingKey,1
				;Set text to 'press a key'..
				invoke	SetDlgItemText,hWin,IDC_CBNEXT,addr cbSetKeyMessage
				;get the keyboard focus off us!, if the user clicks off window, then sets a key, the cursor won't reland here..
				invoke	SendMessage,cbhWin,WM_SETFOCUS,0,0
				;Get a key...
				mov ecx,hWin
				mov	cbhWin,ecx
				invoke	CreateThread,0,0,addr cbGetKey,addr cbNextTrackKey,0,addr cbThreadID
				
				;listen for a key...
				invoke	SetWindowsHookEx,WH_KEYBOARD_LL,addr cbGetKeyC,hInstance,0
				mov cbKeyboardHookID,eax
			.endif
		.elseif	eax==IDC_CBPREV
			.if	ebx==EN_SETFOCUS
				mov ecx,cbSettingKey
				.if	ecx==1
					ret
				.endif
				
				;we are setting a key here, so flag it!
				mov cbSettingKey,1
				;Set text to 'press a key'..
				invoke	SetDlgItemText,hWin,IDC_CBPREV,addr cbSetKeyMessage
				;get the keyboard focus off us!, if the user clicks off window, then sets a key, the cursor won't reland here..
				invoke	SendMessage,cbhWin,WM_SETFOCUS,0,0
				;Watch for a key...
				mov ecx,hWin
				mov	cbhWin,ecx
				invoke	CreateThread,0,0,addr cbGetKey,addr cbPreviousTrackKey,0,addr cbThreadID
				
				;listen for a key...
				invoke	SetWindowsHookEx,WH_KEYBOARD_LL,addr cbGetKeyC,hInstance,0
				mov cbKeyboardHookID,eax
			.endif
		.endif
	.elseif	uMsg == WM_CLOSE
		@quit:
		;Save Prefs!!! TODO
		invoke	UnhookWindowsHookEx,cbKeyboardHookID
		invoke	UnhookWindowsHookEx,cbMouseHookID
		invoke	ReleaseMutex,cbMutexID
		invoke	Shell_NotifyIcon,NIM_DELETE,addr cbIconData
		invoke	DestroyIcon,cbhIcon
		invoke	EndDialog,hWin,0
	.elseif	uMsg==WM_LBUTTONDOWN
		invoke SendMessage,hWin,WM_NCLBUTTONDOWN,HTCAPTION,0
	.elseif	uMsg==CB_ICON_MESSAGE
		.if	lParam==WM_LBUTTONDBLCLK
			invoke	ShowWindow,hWin,SW_SHOW
			invoke	Shell_NotifyIcon,NIM_DELETE,addr cbIconData
			invoke	UnhookWindowsHookEx,cbKeyboardHookID
		.endif
	.elseif	uMsg==WM_INITDIALOG
		invoke	SetWindowText,hWin,addr cbTitle
	.elseif	uMsg==WM_PAINT
		;redraw the editbox windows
		.if	cbPlayPauselParam==0 ;if the play/pause key is still unset
			lea eax,cbSetMe
			jmp @f
		.endif
		invoke	GetKeyNameText,cbPlayPauselParam,addr cbPlayPauseKeyText,31
		.if	eax==0
			lea eax,cbUnknownButton
			jmp @f
		.endif
		lea eax,cbPlayPauseKeyText
		@@:
		invoke	SetDlgItemText,hWin,IDC_CBPLAY,eax
		
		.if	cbNextTracklParam==0
			lea eax,cbSetMe
			jmp @f
		.endif
		invoke	GetKeyNameText,cbNextTracklParam,addr cbNextTrackKeyText,31
		.if	eax==0
			lea eax,cbUnknownButton
			jmp @f
		.endif
		lea eax,cbNextTrackKeyText
		@@:
		invoke	SetDlgItemText,hWin,IDC_CBNEXT,eax
		
		
		.if	cbPreviousTracklParam==0
			lea eax,cbSetMe
			jmp @f
		.endif
		invoke	GetKeyNameText,cbPreviousTracklParam,addr cbPreviousTrackKeyText,31
		.if	eax==0
			lea eax,cbUnknownButton
			jmp @f
		.endif
		lea	eax,cbPreviousTrackKeyText
		@@:
		invoke	SetDlgItemText,hWin,IDC_CBPREV,eax	
	.endif

	xor	eax,eax
	ret
DlgProc	endp

cbGetKey	proc	keymem:DWORD
	@@:
	mov eax,cbTempKey
	.if	eax==0
		invoke	Sleep,100
		jmp @b
	.endif 
	invoke	UnhookWindowsHookEx,cbKeyboardHookID
	
	mov	eax,cbTempKey
	mov	ebx,keymem
	mov	[ebx],eax
	
	lea	eax,cbPlayPauseKey
	lea	ecx,cbNextTrackKey
	lea	edx,cbPreviousTrackKey
	.if	ebx==eax
		mov	eax,cbTemplParam
		mov cbPlayPauselParam,eax
	.elseif	ebx==ecx
		mov	eax,cbTemplParam
		mov	cbNextTracklParam,eax
	.elseif	ebx==edx
		mov	eax,cbTemplParam
		mov	cbPreviousTracklParam,eax
	.endif
	
	;a key has been set, therefore not setting anymore!
	mov cbSettingKey,0
	;clear this for next time..
	mov	cbTempKey,0
	invoke	SendMessage,cbhWin,WM_SETFOCUS,0,0
	invoke	SendMessage,cbhWin,WM_PAINT,0,0
	Ret
cbGetKey EndP

cbGetKeyC	proc	code:DWORD,wParam:DWORD,lParam:DWORD
	.if	wParam==WM_KEYDOWN
		mov eax,lParam
		mov eax,[eax.KBDLLHOOKSTRUCT.vkCode]
		mov cbTempKey,eax
		
		;construct an lParam...
		mov eax,lParam
		mov eax,[eax.KBDLLHOOKSTRUCT.scanCode]
		shl eax,16
		mov ebx,lParam
		mov ebx,[ebx.KBDLLHOOKSTRUCT.flags]
		test bl,0001b
		jz @f
		or eax,0001000000000000000000000000b
		@@:
		mov cbTemplParam,eax
		mov eax,1337
		ret
	.endif

	invoke CallNextHookEx,cbKeyboardHookID,code,wParam,lParam
	Ret
cbGetKeyC EndP

cbKeyMon	proc	code:DWORD,wParam:DWORD,lParam:DWORD
	.if	wParam==WM_KEYDOWN
		mov eax,lParam
		mov eax,[eax]
		.if	eax==cbPlayPauseKey
			invoke	CreateThread,0,0,addr cbPlayPause,0,0,addr cbThreadID
			ret
		.elseif	eax==cbNextTrackKey
			invoke	CreateThread,0,0,addr cbNextTrack,0,0,addr cbThreadID
			ret
		.elseif	eax==cbPreviousTrackKey
			invoke	CreateThread,0,0,addr cbPreviousTrack,0,0,addr cbThreadID
			ret
		.endif
	.endif
	
	invoke CallNextHookEx,cbKeyboardHookID,code,wParam,lParam
	Ret
cbKeyMon EndP

cbMouseMon	proc	code:DWORD,wParam:DWORD,lParam:DWORD
	LOCAL  iTunes:IiTunesList
	LOCAL	level:DWORD
	.if	wParam==WM_MOUSEWHEEL
		invoke	GetKeyState,VK_END
			shr	eax,31
			.if	eax==1
			mov eax,lParam
			mov eax,[eax.MSLLHOOKSTRUCT.mouseData]
			shr	eax,16
			mov ebx,cbVolumeLevel
			.if	ax==120
				add ebx,2
			.elseif	ax==-120
				sub ebx,2
			.endif
			mov cbVolumeLevel,ebx
			invoke	CreateThread,0,0,addr cbVolume,cbVolumeLevel,0,addr cbThreadID
			ret
		.endif
	.endif
	
	invoke CallNextHookEx,cbMouseHookID,code,wParam,lParam
	Ret
cbMouseMon EndP

cbCheckMutex	proc
	invoke	CreateMutex,0,1,addr cbTitle
	invoke	GetLastError
	.if	eax==ERROR_ALREADY_EXISTS
		invoke	MessageBox,0,addr cbAlreadyRunningTitle,addr cbAlreadyRunningMessage,MB_OK or MB_ICONWARNING
		invoke	ExitProcess,1
	.endif
	Ret
cbCheckMutex EndP

cbNextTrack	proc
    	LOCAL  iTunes:IiTunesList
	invoke CoInitialize,NULL
	
	invoke CoCreateInstance,offset CLSID_iTunesApp,NULL,CLSCTX_LOCAL_SERVER,offset IID_IiTunes,addr iTunes
	.if eax==S_OK
		COMETHOD iTunes,IiTunesListVtbl.NextTrack
	.endif

	invoke CoUninitialize
	Ret
cbNextTrack EndP

cbPreviousTrack	proc
    	LOCAL  iTunes:IiTunesList
	invoke CoInitialize,NULL
	
	invoke CoCreateInstance,offset CLSID_iTunesApp,NULL,CLSCTX_LOCAL_SERVER,offset IID_IiTunes,addr iTunes
	.if eax==S_OK
		COMETHOD iTunes,IiTunesListVtbl.PreviousTrack
	.endif

	invoke CoUninitialize
	Ret
cbPreviousTrack EndP

cbPlayPause	proc
    	LOCAL  iTunes:IiTunesList
    	LOCAL WMP:IWMPList
    	
	invoke CoInitialize,NULL
	invoke CoCreateInstance,offset CLSID_iTunesApp,NULL,CLSCTX_LOCAL_SERVER,offset IID_IiTunes,addr iTunes
	.if eax==S_OK
		COMETHOD iTunes,IiTunesListVtbl.PlayPause
	.endif
	invoke CoUninitialize
	
	;START TEST CODE	
	invoke CoInitialize,NULL
	invoke CoCreateInstance,offset CLSID_IWMPPlayer,NULL,CLSCTX_LOCAL_SERVER,offset IID_IWMPControls,addr WMP
	.if eax==S_OK
		;COMETHOD WMP,IWMPListVtbl.Play2
		invoke	MessageBox,0,addr cbAlreadyRunningTitle,addr cbAlreadyRunningMessage,MB_OK or MB_ICONWARNING
	.endif
	invoke CoUninitialize
	
	Ret
cbPlayPause EndP

cbGetVolume	proc
	LOCAL  iTunes:IiTunesList
	LOCAL	level:DWORD
	invoke CoInitialize,NULL
	
	invoke CoCreateInstance,offset CLSID_iTunesApp,NULL,CLSCTX_LOCAL_SERVER,offset IID_IiTunes,addr iTunes
	.if eax==S_OK
		lea	eax,cbVolumeLevel
		push	eax
		COMETHOD iTunes,IiTunesListVtbl.get_SoundVolume
	.endif

	invoke CoUninitialize
	Ret
cbGetVolume EndP

cbVolume		proc	volume:DWORD
	LOCAL  iTunes:IiTunesList
	invoke CoInitialize,NULL
	
	invoke CoCreateInstance,offset CLSID_iTunesApp,NULL,CLSCTX_LOCAL_SERVER,offset IID_IiTunes,addr iTunes
	.if eax==S_OK
		push volume
		COMETHOD iTunes,IiTunesListVtbl.put_SoundVolume
	.endif

	invoke CoUninitialize
	Ret
cbVolume EndP

cbShuffle	proc
    	LOCAL	iTunes:IiTunesList
    	LOCAL	PlayList:IiTunesList
	invoke CoInitialize,NULL
	
	invoke CoCreateInstance,offset CLSID_iTunesApp,NULL,CLSCTX_LOCAL_SERVER,offset IID_IiTunes,addr iTunes
	.if eax==S_OK
		COMETHOD	iTunes,IiTunesListVtbl.Play
		lea	eax,PlayList
		push	eax
		COMETHOD iTunes,IiTunesListVtbl.get_CurrentPlaylist
		mov	eax,cbShuffleSet
		.if	eax==1
			mov eax,-1
		.else
			xor	eax,eax
		.endif
		push eax
		COMETHOD PlayList,IPlaylistVtbl.put_Shuffle
	.endif

	invoke CoUninitialize
	Ret
cbShuffle EndP

cbAbout	proc
	invoke	DialogBoxParam, hInstance, IDC_CBABOUTDIALOG, 0, ADDR cbAboutC, 0
	Ret
cbAbout EndP

cbAboutC	proc	hWin	:DWORD,
		uMsg	:DWORD,
		wParam	:DWORD,
		lParam	:DWORD
		
	.if	uMsg == WM_COMMAND
		.if	wParam==IDC_CBABOUTOK
			invoke	EndDialog,hWin,0
		.endif
	.endif
	
	xor eax,eax
	ret
cbAboutC EndP
	
end start